#ifndef SPOT_LIGHT_3D_HPP
#define SPOT_LIGHT_3D_HPP
#include "OmniLight3D.hpp"


class SpotLight3D : public Object3D
{
	const Shader* shader;
public:
	SpotLight3D(const flecs::world& w, flecs::entity ecs, uint8_t num, const Shader* shader, const vec3& color, float cut_off, float power, std::string modelPath = "") : Object3D(w, ecs, modelPath), shader(shader)
	{
		shader->use();
		getEntity().set<uint8_t>(num);
		getEntity().add<vec3, tagColor>();

		setPower(power);
		setCutOff(cut_off);
		setColor(color);
	};
	SpotLight3D(std::string name, const flecs::world& w, flecs::entity ecs, uint8_t num, const Shader* shader, const vec3& color, float cut_off, float power, std::string modelPath = "") : Object3D(name, w, ecs, modelPath), shader(shader)
	{
		shader->use();
		getEntity().set<uint8_t>(num);
		getEntity().add<vec3, tagColor>();

		setPower(power);
		setCutOff(cut_off);
		setColor(color);
	}

	inline const uint8_t getID()
	{
		return *getEntity().get<uint8_t>();
	}
	inline const float getPower()
	{
		return *getEntity().get<float, tagPower>();
	}
	inline const vec3* getColor()
	{
		return getEntity().get<vec3, tagColor>();
	}
	inline float getCutOff()
	{
		return *getEntity().get<float, tagCutOff>();
	}

	inline void teleport(const glm::vec3& position)
	{
		mat4 mat = *getMatrix();
		mat[3] = glm::vec4(position, mat[3].w);

		getEntity()
			.set<mat4>(mat)
			.set<vec3, tagPosition>(position);
		shader->setVec3("spotlights[" + std::to_string(getID()) + "].position", getPosition());
	}
	inline void move(const glm::vec3& move)
	{
		getEntity()
			.set<vec3, tagPosition>(getPosition() + move)
			.set<mat4>(glm::translate(*getMatrix(), move));
		shader->setVec3(" spotlights[" + std::to_string(getID()) + "].position", getPosition());
	}
	inline void lookAt(const glm::vec3& target)
	{
		auto position = getPosition();
		auto look_at = glm::lookAt(-position, -target, glm::vec3(0.f, 1.f, 0.f));

		auto mat(glm::scale(glm::mat4(1.f), getScale()) * glm::inverse(look_at));
		mat[3] = glm::vec4(position, mat[3].w);

		auto quatern = glm::toQuat(mat);
		getEntity()
			.set<mat4>(mat)
			.set<quat, tagRotation>(quatern);

		auto direction = glm::normalize(glm::mat3(mat) * vec3(0.f, 0.f, 1.f));
		shader->setVec3("spotlights[" + std::to_string(getID()) + "].direction", direction);
	}
	inline void setDirection(const glm::vec3& target)
	{
		auto look_at = glm::lookAt(glm::vec3(0.f), -target, glm::vec3(0.f, 1.f, 0.f));
		glm::mat4 mat(glm::scale(glm::mat4(1.f), getScale()) * glm::inverse(look_at));

		mat[3] = glm::vec4(getPosition(), mat[3].w);
		getEntity()
			.set<mat4>(mat)
			.set<quat, tagRotation>(glm::toQuat(mat));

		auto direction = glm::mat3(mat) * glm::vec3(0.f, 0.f, 1.f);
		shader->setVec3("spotlights[" + std::to_string(getID()) + "].direction", direction);
	}
	inline void setPower(float power)
	{
		getEntity().set<float, tagPower>(power);
		shader->setFloat("spotlights[" + std::to_string(getID()) + "].power", power);
	}
	inline void setColor(const vec3& color)
	{
		getEntity().set<vec3, tagColor>(color);
		shader->setVec3("spotlights[" + std::to_string(getID()) + "].specular", color * glm::vec3(1.0f));
		shader->setVec3("spotlights[" + std::to_string(getID()) + "].diffuse", color * glm::vec3(0.8f));
		shader->setVec3("spotlights[" + std::to_string(getID()) + "].ambient", color * glm::vec3(0.2f));
	}
	inline void setCutOff(float value)
	{
		getEntity().set<float, tagCutOff>(value);
		shader->setFloat("spotlights[" + std::to_string(getID()) + "].cutoff", value);
	}
};
#endif // SPOT_LIGHT_3D_HPP